/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2020 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/
#ifndef OSGEARTH_DRAGGERS_H
#define OSGEARTH_DRAGGERS_H

#include <osgEarth/Common>
#include <osgEarth/GeoPositionNode>
#include <osgEarth/GeoData>
#include <osgEarth/TileKey>
#include <osgEarth/MapNodeObserver>
#include <osg/MatrixTransform>
#include <osg/ShapeDrawable>
#include <osgGA/GUIEventHandler>
#include <osgManipulator/Projector>
#include <osgEarth/Terrain>

namespace osgEarth
{
    class MapNode;
    class Terrain;

    /**
     * Dragger is a handle you can use to control things in the scene.
     * You drag it around with the mouse and it fires PositionChangedCallback's
     * that you can listen to to repond to.
     */
    class OSGEARTH_EXPORT Dragger : public GeoPositionNode
    {
    public:
        /**
        * Callback that is fired when the position changes
        */
        struct PositionChangedCallback : public osg::Referenced
        {
        public:
            virtual void onPositionChanged(const Dragger* sender, const osgEarth::GeoPoint& position) {};
            virtual ~PositionChangedCallback() { }
        };

        typedef std::list< osg::ref_ptr<PositionChangedCallback> > PositionChangedCallbackList;

        enum DragMode
        {
          DRAGMODE_HORIZONTAL,
          DRAGMODE_VERTICAL
        };

        Dragger( MapNode* mapNode, int modKeyMask=0, const DragMode& defaultMode=DRAGMODE_HORIZONTAL );

        /** dtor */
        virtual ~Dragger();

        /** Sets the map position of the dragger, optionally firing a PositionChanged event. */
        void setPosition(const osgEarth::GeoPoint& position, bool fireEvents);

        /** Is the user currently dragging? */
        bool getDragging() const;

        /** Is the mouse hovering over this dragger? */
        bool getHovered() const;

        /** Mod key to require to activate the dragget */
        void setModKeyMask(int mask) { _modKeyMask = mask; }
        int getModKeyMask() const { return _modKeyMask; }

        /** Drag mode */
        void setDefaultDragMode(const DragMode& mode) { _defaultMode = mode; }
        DragMode& getDefaultDragMode() { return _defaultMode; }

        /** Minimum vertical distance for vertical dragging */
        void setVerticalMinimum(double min) { _verticalMinimum = min; }
        double getVerticalMinimim() const { return _verticalMinimum; }
        
        /** Runs when the mouse intersects/hovers over the dragger */
        virtual void enter();

        /** Runs when the mouse leaves the dragger */
        virtual void leave();

        /** Sets the main color of the dragger */
        virtual void setColor( const osg::Vec4f& color ) =0;

        /** Sets the color to use when the dragger is entered */
        virtual void setPickColor( const osg::Vec4f& color ) =0;

        /** Add a callback that runs whenever the user moves the dragger */
        void addPositionChangedCallback( PositionChangedCallback* callback );

        /** Remove a callback. */
        void removePositionChangedCallback( PositionChangedCallback* callback );


    public: // GeoPositionNode

        virtual void setPosition(const GeoPoint& point);

    public: // osg::Node

        virtual void traverse(osg::NodeVisitor& nv);

    protected:
        virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
        void setHover( bool hovered);
        void firePositionChanged();
        
        bool _dragging;
        bool _hovered;
        PositionChangedCallbackList _callbacks;

        osg::ref_ptr<  osgManipulator::LineProjector >  _projector;
        osgManipulator::PointerInfo  _pointer;
        osg::Vec3d _startProjectedPoint;
        bool _elevationDragging;
        int _modKeyMask;
        DragMode _defaultMode;
        double _verticalMinimum;
    };

    /**********************************************************/
    class OSGEARTH_EXPORT SphereDragger : public Dragger
    {
    public:
        SphereDragger(MapNode* mapNode);

        /** dtor */
        virtual ~SphereDragger() { }

        const osg::Vec4f& getColor() const;

        void setColor(const osg::Vec4f& color);

        const osg::Vec4f& getPickColor() const;

        void setPickColor(const osg::Vec4f& pickColor);

        float getSize() const;
        void setSize(float size);

        virtual void enter();

        virtual void leave();

    protected:

        void updateColor();
        osg::ShapeDrawable* _shapeDrawable;
        osg::Vec4f _pickColor;
        osg::Vec4f _color;
        float _size;
    };


}

#endif // OSGEARTH_DRAGGERS_H
